package com.chenfy.ioc_compiler;


import com.chenfy.ioc_annotation.BindLayout;
import com.chenfy.ioc_annotation.BindView;
import com.chenfy.ioc_annotation.ViewInject;
import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.TypeSpec;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;

@AutoService(Processor.class)
public class IocBindingProcessor extends AbstractProcessor {
    private static final String PACKAGE_NAME = "com.chenfy.iocdemo";
    private static final String CLASS_NAME = "BindHelper";
    private static final String BASE_ACTIVITY_NAME = "com.chenfy.touchtransfer_android.base.BaseActivity";

    Filer filer;

    @Override
    public synchronized void init(ProcessingEnvironment processingEnvironment) {
        super.init(processingEnvironment);
        filer = processingEnvironment.getFiler();
    }

    @Override
    public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
        System.out.println("--------process-------");
        Map<String, List<Element>> elementMap = new HashMap<>();
        // 收集信息
        // rootElements, 可以理解就是类
        for (Element element : roundEnvironment.getRootElements()) {
            // app model 的包名
            if (element instanceof TypeElement) {
                TypeElement typeElement = (TypeElement) element;
                // 必须继承BaseActivity 才能使用这个注解
                if (BASE_ACTIVITY_NAME.equals(typeElement.getSuperclass().toString())){

                    String fullName = typeElement.getQualifiedName().toString(); // 权限定类名
                    List<Element> elements = elementMap.get(fullName);
                    if (elements == null) {
                        elements = new ArrayList<>();
                        elementMap.put(fullName, elements);
                    }
                    elements.add(element);
                }
            }

        }

        // 生成壳子 BindHelper.java
        // 需要实现ViewInject 接口
        // 动态生成对应的类名
        ClassName className = ClassName.get(PACKAGE_NAME, CLASS_NAME);
        TypeSpec.Builder builder = TypeSpec.classBuilder(className);
        builder.addSuperinterface(ViewInject.class);

        // 生成方法
        MethodSpec.Builder methodBuild = MethodSpec.methodBuilder("inject") // 接口方法名
                .addParameter(ClassName.get(Object.class), "activity")
                .addModifiers(Modifier.PUBLIC);
        // 生成方法体
        boolean hasBinding = false;

        for (String activityName : elementMap.keySet()) {
            methodBuild.addCode("if (activity instanceof " + activityName + " ) {\n");
            List<Element> elements = elementMap.get(activityName);
            for (Element element : elements) {
                // setContentView
                BindLayout bindLayout = element.getAnnotation(BindLayout.class);

                if (bindLayout != null) {
                    // 这个类被BindLayout 修饰
                    int layoutID = bindLayout.value();
                    if (layoutID != 0) {
                        methodBuild.addStatement("((android.app.Activity)activity).setContentView($L)"
                                , layoutID);
                    }
                }


                for (Element enclosedElement : element.getEnclosedElements()) {
                    // 返回这个element(类)中所有封装的元素(属性,方法啥的)
                    // 这些应该都是被注解的元素
                    if (enclosedElement.getKind() == ElementKind.FIELD) {
                        BindView bindView = enclosedElement.getAnnotation(BindView.class);
                        if (bindView != null) {
                            hasBinding = true;
                            // 自动findViewById
                            methodBuild.addStatement("(($N)activity).$N = (($N)activity).findViewById($L)",
                                    activityName, enclosedElement.getSimpleName(), activityName, bindView.value());
                            boolean click = bindView.click();
                            if (click) {
                                // 设置setClickListener
                                methodBuild.addStatement("(($N)activity).$N.setOnClickListener(($N)activity)",
                                        activityName,
                                        enclosedElement.getSimpleName(), activityName);
                            }
                        }
                    }
                }
            }
            methodBuild.addCode("}\n");
        }


        TypeSpec builtClass = builder
                .addModifiers(Modifier.PUBLIC)

                .addMethod(methodBuild.build())
                .build();

        if (hasBinding) {
            try {
                System.out.println("--------写入-------");
                JavaFile.builder(PACKAGE_NAME, builtClass)
                        .build().writeTo(filer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return true;
    }

    // 返回支持的注解类型
    // 写法固定
    @Override
    public Set<String> getSupportedAnnotationTypes() {
        Set<String> annotationTypes = new LinkedHashSet<>();
        // 支持 BindView 这种注解(在ioc-annotation模块中定义的)
        annotationTypes.add(BindView.class.getCanonicalName());
        annotationTypes.add(BindLayout.class.getCanonicalName());
        return annotationTypes;
    }

    // 返回支持的源码版本
    @Override
    public SourceVersion getSupportedSourceVersion() {
        return SourceVersion.latestSupported(); // 写法固定
    }
}
